<?php

namespace App\Listeners;

use App\Enums\AssetSourceType;
use App\Enums\AssetStatus;
use App\Enums\ProductType;
use App\Enums\TokenLockType;
use App\Enums\TokenStatus;
use App\Enums\TransStatus;
use App\Events\OrderPaid;
use App\Models\Asset;
use App\Models\BlindBoxItem;
use App\Models\BlindBoxRecord;
use App\Models\ProductToken;
use App\Services\WalletService;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Arr;

class OrderPaidListener implements ShouldQueue
{
    /** @var WalletService */
    private $walletService;

    /**
     * Create the event listener.
     */
    public function __construct(WalletService $walletService)
    {
        $this->walletService = $walletService;
    }

    /**
     * Handle the event.
     */
    public function handle(OrderPaid $event)
    {
        $order = $event->order;
        $lock = \Cache::lock('product_order_paid' . $order->product_id, 60);

        try {
            $order->refresh();
            // 检查资产转移状态
            if ($order->trans_status) {
                return;
            }
            $product = $assetProduct = $order->product;

            $lock->block(5); // 如果获取不到锁，则等待5秒
            if ($product->type->is(ProductType::BLIND_BOX())) {
                # 处理肓盒订单
                // 从肓合队列中查询对应的商品和token

                /** @var BlindBoxItem[] $blindBoxItems */
                $blindBoxItems = BlindBoxItem::query()
                    ->with(['product_token'])
                    ->where('blind_box_id', $product->id)->where('status', TokenStatus::INIT)
                    ->limit($order->quantity)
                    ->orderBy('sequence')->get();

                $batchRecords = [];
                foreach ($blindBoxItems as $blindBoxItem) {
                    $batchRecords[] = [
                        'user_id' => $order->user_id,
                        'order_id' => $order->id,
                        'blind_box_id' => $product->id,
                        'asset_product_id' => $blindBoxItem->product_id,
                        'asset_token_id' => $blindBoxItem->product_token->token_id,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ];
                }

                \DB::transaction(function () use ($blindBoxItems, $batchRecords) {
                    BlindBoxItem::query()->whereIn('id', Arr::pluck($blindBoxItems, 'id'))->update([
                        'status' => TokenStatus::SOLD(),
                    ]);
                    BlindBoxRecord::query()->insert($batchRecords);
                });

                return;
            }

            # 处理普通订单
            // 资产转移
            $quantity = $order->quantity;
            $tokens = ProductToken::query()->where('product_id', $order->product_id)
                ->where('status', TokenStatus::INIT)
                ->limit($quantity)->get();
            $productTokenIds = Arr::pluck($tokens, 'id');
            $tokenIds = Arr::pluck($tokens, 'token_id');
            if (count($tokenIds) != $quantity) {
                $order->trans_status = TransStatus::FAILED;
                $order->trans_fail_reason = '剩余token数量不足';
                $order->save();

                return;
            }
            // 锁定
            ProductToken::query()->whereIn('id', $productTokenIds)->update([
                'lock_type' => TokenLockType::ORDER,
                'lock_refer_id' => $order->id,
                'status' => TokenStatus::LOCKED,
            ]);
        } catch (LockTimeoutException $e) {
            throw new \Exception('并发请求过多，请稍后再试');
        } finally {
            $lock->release();
        }

        // 请求转账接口
        try {
            $fromMobile = ''; // 默认使用商品发布时的收款方为付款方
            if ($product->resale_user) {
                $fromMobile = $product->resale_user->mobile;
            }
            $transResult = $this->walletService->transfer(
                $fromMobile,
                $assetProduct->contract_address,
                $tokenIds,
                $order->user->wallet_address
            );

            // 更新token状态
            foreach ($tokens as $token) {
                $transHash = Arr::get($transResult, $token->token_id, 'trans_hash_not_return');
                $token->status = TokenStatus::SOLD;
                $token->save();
                $asset = new Asset();
                // 保存资产信息
                $asset->user_id = $order->user_id;
                $asset->source_type = AssetSourceType::ORDER;
                $asset->source_id = $order->id;
                $asset->contract_address = $assetProduct->contract_address;
                $asset->token_id = $token->token_id;
                $asset->trans_hash = $transHash;
                $asset->asset_status = AssetStatus::HOLD;
                $asset->save();
            }
            $order->trans_status = TransStatus::SUCCESS;
            $order->trans_fail_reason = '';
            $order->save();
        } catch (\Exception $e) {
            $failMsg = '资产转账失败: ' . $e->getMessage();
            ProductToken::query()->whereIn('id', $productTokenIds)->update([
                'status' => TokenStatus::FAILED,
            ]);

            $order->trans_status = TransStatus::FAILED;
            $order->trans_fail_reason = substr($failMsg, 0, 200);
            $order->save();
        }

        if ($product->type->is(ProductType::RESALE())) {
            // 转售订单，更新原Token状态
            Asset::query()
                ->where('user_id', $product->resale_user_id)
                ->whereIn('token_id', $tokenIds)
                ->where('asset_status', AssetStatus::ON_SALE)
                ->update(['asset_status' => AssetStatus::SOLD]);
        }
    }
}
